//
// Created by win10 on 2021/1/4.
//

#ifndef BACKHEADSHARE_TCPSERVER_H
#define BACKHEADSHARE_TCPSERVER_H

#include <sys/socket.h>
#include <sys/epoll.h>
#include <cstdio>
#include <functional>
#include <arpa/inet.h>
#include <fcntl.h>
#include <string>
#include <unistd.h>

class TcpServer {
private:
    std::function<void(std::string &&, int)> _handler;


    static int setNonBlocking(int fd) {
        int old_flag = fcntl(fd, F_GETFL, 0);
        int new_flag = old_flag | O_NONBLOCK;
        return fcntl(fd, F_SETFL, new_flag);
    }


public:
    TcpServer() = default;

    ~TcpServer() = default;

    std::function<void(std::string &&, int)> &onMessage() { return _handler; }

    [[noreturn]] void run(u_int16_t port, const char *ip) {
        int server_fd;
        if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
            perror("socket failed.");

        if ((setNonBlocking(server_fd)) < 0)
            perror("set nonblocking failed.");

        sockaddr_in server_addr{};
        inet_pton(AF_INET, ip, &server_addr.sin_addr.s_addr);
        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(port);
        if ((bind(server_fd, (sockaddr *) &server_addr, sizeof(server_addr))) < 0)
            perror("bind failed.");

        if ((listen(server_fd, 5)) < 0)
            perror("listen failed.");

        int epoll_fd;
        if ((epoll_fd = epoll_create(1024)) < 0)
            perror("epoll_create failed.");

        epoll_event ev{};
        ev.data.fd = server_fd;
        ev.events = EPOLLIN; // LT, IN
        if ((epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev)) < 0)
            perror("add server_fd failed.");

        auto events = new epoll_event[5000];
        char buffer[2048]; //2KB user buffer
        while (true) {
            int n_fd;
            if ((n_fd = epoll_wait(epoll_fd, events, 5000, -1)) < 0)
                perror("epoll_wait failed.");

            for (int i = 0; i < n_fd; ++i) {
                if (events[i].data.fd == server_fd) {
                    //accept
                    sockaddr_in client_addr{};
                    socklen_t addr_len = sizeof(client_addr);
                    int client_fd;
                    if ((client_fd = accept(server_fd, (sockaddr *) &client_addr, &addr_len)) < 0)
                        perror("accept failed.");
                    if (setNonBlocking(client_fd) < 0)
                        perror("set nonblocking failed.");
                    ev.data.fd = client_fd;
                    ev.events = EPOLLIN;
                    if ((epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev)) < 0)
                        perror("add client_fd failed.");

                } else { // 客户端连接可读

                    int client_fd = events[i].data.fd;
                    int n_recv = recv(client_fd, &buffer, 2048, 0);
                    if (n_recv < 0) perror("recv error.");
                    if (n_recv == 0) {
                        epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, nullptr);
                        close(client_fd);
                    }
                    if (n_recv > 0) {
                        std::string buf_string(buffer, n_recv);
                        _handler(std::move(buf_string), client_fd);
                    }
                }
            }
        }
    }


};


#endif //BACKHEADSHARE_TCPSERVER_H
